flutter ios小组件 flutter 安卓桌面小部件 您所在的位置:网站首页 安卓 桌面小工具怎么删除 flutter ios小组件 flutter 安卓桌面小部件

flutter ios小组件 flutter 安卓桌面小部件

2024-07-01 05:31| 来源: 网络整理| 查看: 265

flutter ios小组件 flutter 安卓桌面小部件_flutter ios小组件

通过此篇文章,你将了解到:

Flutter动画实现灵动岛;Flutter如何开发一个置顶可自由拖拽的小工具;分享一些关于灵动岛的想法。

⚠️本文为稀土掘金技术社区首发签约文章,14天内禁止转载,14天后未获授权禁止转载,侵权必究!

前言

Flutter开发Windows应用已经见怪不怪了,我觉得可以尝试做一些小工具。恰逢近期最近苹果iphone 14系列推出“灵动岛”,这个酷炫的组件瞬间引起很多关注;而且看到一些前端博客用css实现灵动岛的效果;作为Flutter的忠实拥护者,前端能写的Flutter必须能写!

灵动岛效果实现

flutter ios小组件 flutter 安卓桌面小部件_flutter_02

小药丸放大 小药丸放大的效果可以拆分为两步:横向放大+惯性缩放回弹。需要两个动画和控制器,当放大动画执行完毕的时候,执行缩放动画,从1.04到1.0。// 初始化变量 late Animation _animation; late AnimationController _animationController; AnimationStatus status = AnimationStatus.forward; late Animation _scaleAnimation; late AnimationController _scaleAnimationController;void initState() { super.initState(); _animationController = AnimationController( duration: const Duration(milliseconds: 500), vsync: this, ); _animation = Tween( begin: Size(104.w, EnvConfig.relHeight), end: Size(168.w, EnvConfig.relHeight), ).animate(_animationController); _scaleAnimationController = AnimationController( duration: const Duration(milliseconds: 300), vsync: this, ); _scaleAnimation = Tween( begin: 1, end: 1, ).animate(_scaleAnimationController); // 放大动画执行完毕,开始缩放动画,从1.04到1.0 _animationController.addStatusListener((status) { this.status = status; if (status == AnimationStatus.completed) { _scaleAnimation = Tween( begin: count == 3 ? 1.04 : 1.06, end: 1, ).animate(_scaleAnimationController); _scaleAnimationController.forward(from: 0); } }); }

布局上使用AnimatedBuilder监听animate值的变化,设置小药丸的宽高以达到放大和缩放效果。

AnimatedBuilder( animation: _scaleAnimation, builder: (context, _) => AnimatedBuilder( animation: _animation, builder: (context, _) => Container( width: _animation.value.width * _scaleAnimation.value, height: _animation.value.height * _scaleAnimation.value, clipBehavior: Clip.antiAliasWithSaveLayer, decoration: BoxDecoration( color: Colors.black, borderRadius: BorderRadius.all( Radius.circular(15.h), ), ), ), ), ),i形分离效果 在小药丸后面要分离出一个小圆圈,从而实现i形的效果;这里也需要一个动画控制器,在布局上我们选择Stack和Positioned。分离过程就是小圆圈的右边距一直往负的方向放大,实现向右移出和向左缩回。late Animation _ballAnimation; late AnimationController _ballAnimationController;_ballAnimationController = AnimationController( duration: const Duration(milliseconds: 600), vsync: this, ); _ballAnimation = Tween(begin: 0, end: -EnvConfig.relHeight - 5) .chain(CurveTween(curve: Curves.easeInOut)) .animate(_ballAnimationController); // 当药丸缩回的过程中,执行分离动画 _animationController.addListener(() { if (count == 2 && status == AnimationStatus.reverse && _animationController.value > 0.25 && _animationController.value < 0.3) { _ballAnimationController.forward(from: 0); } });

上面是动画的过程,我们再看下布局的代码:

AnimatedBuilder( animation: _ballAnimation, builder: (context, _) => Stack(clipBehavior: Clip.none, children: [ AnimatedBuilder( // .... 小药丸 .... ), Positioned( top: 0, right: _ballAnimation.value, child: Container( width: EnvConfig.relHeight, height: EnvConfig.relHeight, decoration: const BoxDecoration( shape: BoxShape.circle, color: Colors.black, ), ), ), ]), ),

动画其实非常简单,也没啥好讲的,重点在分享如何作为一个小工具。具体源码见文末仓库。

将应用配置为小工具【Windows端】

这里的前提是基于上一篇文章:做好屏幕的适配。 在windows上,小工具就是一个普通应用【这跟Android window_manager的机制是不一样的】。不过我们需要把宽高、位置设置好;同时还需要保证小工具置顶、没有状态栏图标。 这里我们依然用到了window_manager的插件,每个步骤都有对应注释。

static late double relHeight; static initWindow(List args, {Size? screenSize}) async { // 注释:获取屏幕真实大小 Display primaryDisplay = await screenRetriever.getPrimaryDisplay(); relHeight = primaryDisplay.size.height * 0.04; double relWidth = relHeight * 8; final displaySize = Size(relWidth, relHeight * 1.06); await setSingleInstance(args); WindowManager w = WindowManager.instance; await w.ensureInitialized(); WindowOptions windowOptions = WindowOptions( size: displaySize, minimumSize: displaySize, alwaysOnTop: true, // 注释:设置置顶 titleBarStyle: TitleBarStyle.hidden, // 注释:去除窗口标题栏 skipTaskbar: true // 注释:去除状态栏图标 ); w.waitUntilReadyToShow(windowOptions, () async { double w1 = (primaryDisplay.size.width - relWidth) / 2; await w.setBackgroundColor(Colors.transparent); await w.setPosition(Offset(w1, 10)); // 注释:设置居中 await w.show(); await w.focus(); await w.setAsFrameless(); }); }

这样我们就可以得到一个very good的小组件啦!

flutter ios小组件 flutter 安卓桌面小部件_android_03

将应用配置为小工具【Android端】

Android小组件与Windows可是大有不同。由于Google基于安全的限制,Android应用必须是全屏且不允许穿透点击,因此Android的小组件一般都是依附于悬浮窗来开发的,即windows_manager。 Flutter只是一个UI框架,自然也不能脱离Android本身的机制,因此我们需要在原生层创建一个悬浮窗,然后创建一个Flutter engine来吸附Flutter的UI。

创建后台服务 创建一个悬浮窗,实现步骤注意看其中的注释package com.karl.open.desktop_app import android.annotation.SuppressLint import android.app.Service import android.content.Intent import android.graphics.PixelFormat import android.os.IBinder import android.util.DisplayMetrics import android.view.LayoutInflater import android.view.MotionEvent import android.view.ViewGroup import android.view.WindowManager import android.widget.FrameLayout import com.karl.open.desktop_app.utils.Utils import io.flutter.embedding.android.FlutterSurfaceView import io.flutter.embedding.android.FlutterView import io.flutter.embedding.engine.FlutterEngine import io.flutter.embedding.engine.FlutterEngineGroup import io.flutter.embedding.engine.dart.DartExecutor import io.flutter.view.FlutterMain.findAppBundlePath class WindowsService : Service() { // Flutter引擎组,可以自动管理引擎的生命周期 private lateinit var engineGroup: FlutterEngineGroup private lateinit var engine: FlutterEngine private lateinit var flutterView: FlutterView private lateinit var windowManager: WindowManager private val metrics = DisplayMetrics() private lateinit var inflater: LayoutInflater @SuppressLint("InflateParams") private lateinit var rootView: ViewGroup private lateinit var layoutParams: WindowManager.LayoutParams override fun onCreate() { super.onCreate() layoutParams = WindowManager.LayoutParams( Utils.dip2px(this, 168.toFloat()), Utils.dip2px(this, 30.toFloat()), WindowManager.LayoutParams.TYPE_SYSTEM_ALERT, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT ) // 初始化变量 windowManager = this.getSystemService(Service.WINDOW_SERVICE) as WindowManager inflater = this.getSystemService(Service.LAYOUT_INFLATER_SERVICE) as LayoutInflater rootView = inflater.inflate(R.layout.floating, null, false) as ViewGroup engineGroup = FlutterEngineGroup(this) // 创建Flutter Engine val dartEntrypoint = DartExecutor.DartEntrypoint(findAppBundlePath(), "main") val option = FlutterEngineGroup.Options(this).setDartEntrypoint(dartEntrypoint) engine = engineGroup.createAndRunEngine(option) // 设置悬浮窗的位置 @Suppress("Deprecation") windowManager.defaultDisplay.getMetrics(metrics) setPosition() @Suppress("ClickableViewAccessibility") rootView.setOnTouchListener { _, event -> when (event.action) { MotionEvent.ACTION_DOWN -> { layoutParams.flags = layoutParams.flags or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE windowManager.updateViewLayout(rootView, layoutParams) true } else -> false } } engine.lifecycleChannel.appIsResumed() // 为悬浮窗加入布局 rootView.findViewById(R.id.floating_window) .addView( flutterView, ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT ) ) windowManager.updateViewLayout(rootView, layoutParams) } private fun setPosition() { // 设置位置 val screenWidth = metrics.widthPixels val screenHeight = metrics.heightPixels layoutParams.x = (screenWidth - layoutParams.width) / 2 layoutParams.y = (screenHeight - layoutParams.height) / 2 windowManager.addView(rootView, layoutParams) flutterView = FlutterView(inflater.context, FlutterSurfaceView(inflater.context, true)) flutterView.attachToFlutterEngine(engine) } }唤起悬浮窗组件 直接通过adb指令唤起即可

adb shell am start-foreground-service -n com.karl.open.desktop_app/com.karl.open.desktop_app.WindowsService

注意通过服务唤起悬浮窗,Android要求必须是系统应用,因此大家在使用的时候还需要配置下系统签名;Flutter engine必须使用FlutterEngineGroup进行托管,否则静置一段时间后,engine就会被系统回收!!!关于灵动岛的一些思考

windows版本的灵动岛组件,实现起来其实是比较简单的。但是我在思考,假设我作为一个OS开发者,我该怎么看待iPhone的这个软件创新?

iPhone这个灵动岛的问世,其实把用户对状态栏的认知颠覆了:原来平时用来看时间电量的地方还能这么玩;这个创新能否带动整个移动端、甚至桌面端状态栏等工具的改革?虽说创新,但目前从各种测评来看,这个工具很少有应用接入,连iOS自己的软件都很多没有接入。着实是有点鸡肋的,而且用户还要去学习如何使用这个灵动岛,当应用更多的接入进来,用户的教育成本会变得更高,降低使用体验。所以iPhone为啥敢开拓创新做这个至少目前很鸡肋的工具呢?iPhone官方如何去推广灵动岛,让更多用户接受?

上面这几个问题,也是我一直在思考的。但其实是环环相扣的,首先能否引领新的交互改革,这个取决于市场的接受度。而市场的接受度,除了果粉引以为傲的“别人没有而我有”,还要做到真正的实用:iOS自身更多的软件接入,让灵动岛功能更完善。 用户习惯了用这个工具,大量软件就必须为了用户而作。 同时按照iPhone的营销手段,会大量利用iPhone的用户心理,不断放大这个灵动岛的格调,很多软件为了俘获用户,甚至会专门为灵动岛做一些扩充的功能,从而吸引很多用户。【目前已有一些软件在做这个事情了】

而假设我是OS开发者,如果我要去做这个工具,首先我的用户基数要足够大,同时让工具提供简单且实用的功能,真正把投入产出比做好,而且真正得服务于用户。酷炫与否交给营销去推广,真正对用户有用的东西,才是底子所在!



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有